package io.kubesphere.devops.service.impl;

import io.kubesphere.devops.common.context.MergingContext;
import io.kubesphere.devops.core.UuidUtils;
import io.kubesphere.devops.dao.UserMergingDao;
import io.kubesphere.devops.dao.UserMergingDetailDao;
import io.kubesphere.devops.model.MergeAction;
import io.kubesphere.devops.model.PartitionAction;
import io.kubesphere.devops.model.UserMerging;
import io.kubesphere.devops.model.UserMergingDetail;
import io.kubesphere.devops.service.MergeCommitEvent;
import io.kubesphere.devops.service.MergeEvent;
import io.kubesphere.devops.service.MergeLogService;
import io.kubesphere.devops.service.MergeRevertEvent;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.sql.Timestamp;
import java.util.List;
import java.util.stream.Collectors;

/**
 * <p> MergeLogServiceImpl 合并记录服务
 *
 * @author gaochuan
 * @version 0.1.0
 * @date 2021/11/22 14:50
 * @since 2021/11/22
 */
@Service
@Slf4j
public class MergeLogServiceImpl implements MergeLogService {
    private final UserMergingDao userMergingDao;
    private final UserMergingDetailDao userMergingDetailDao;

    public MergeLogServiceImpl(UserMergingDao userMergingDao, UserMergingDetailDao userMergingDetailDao) {
        this.userMergingDao = userMergingDao;
        this.userMergingDetailDao = userMergingDetailDao;
    }

    @Override
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
    public UserMerging mergeLog() {
        String mergingId = UuidUtils.getRandomUUIDString();
        UserMerging merging = new UserMerging(mergingId, "1", "1", "0");
        userMergingDao.saveUserMergingLog(merging);
        MergingContext.bindXID(mergingId);
        return merging;
    }

    @Override
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
    public void mergeLog(MergeEvent event) {
        Timestamp current = new Timestamp(System.currentTimeMillis());

        UserMergingDetail detail = UserMergingDetail.builder().userMergingId(event.getUserMergingId())
                .userMergingDetailId(event.getUserMergingDetailId())
                .userMergingTimestamp(current)
                .datasourceId(event.getPartitionName())
                .status("0")
                .build();
        userMergingDetailDao.saveMergingDetail(detail);
    }

    public UserMerging queryMergeOverview(String userMergingId) {

        return userMergingDao.getById(userMergingId);
    }

    @Override
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
    public void mergingBranchCommit(MergeCommitEvent mergingBranchCommit) {

        this.mergingBranchSuccess(mergingBranchCommit.getUserMergingId(), mergingBranchCommit.getUserMergingDetailId());
    }

    @Override
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
    public void mergingBranchRevert(MergeRevertEvent mergeRevertEvent) {
        this.mergingBranchFail(mergeRevertEvent.getUserMergingId(), mergeRevertEvent.getUserMergingDetailId());
    }

    private void mergingBranchFail(String userMergingId, String userMergingDetailId) {
        userMergingDetailDao.updateStatus(userMergingDetailId, "2");
        List<UserMergingDetail> mergingDetails = this.queryMergeDetail(userMergingId);
        if (mergingDetails.stream().anyMatch(userMergingDetail -> userMergingDetail.getStatus().equals("1"))) {
            return;
        }

        this.mergingFail(userMergingId);
    }

    public void mergingSuccess(String userMergingId) {
        userMergingDao.updateStatus(userMergingId, "1");
    }

    public void mergingFail(String userMergingId) {
        userMergingDao.updateStatus(userMergingId, "2");
    }

    public void mergingBranchSuccess(String userMergingId, String userMergingDetailId) {
        userMergingDetailDao.updateStatus(userMergingDetailId, "1");
        List<UserMergingDetail> mergingDetails = this.queryMergeDetail(userMergingId);
        if (mergingDetails.stream().anyMatch(userMergingDetail -> userMergingDetail.getStatus().equals("0"))) {
            return;
        }

        this.mergingSuccess(userMergingId);
    }

    public List<UserMergingDetail> queryMergeDetail(String userMergingId) {
        return userMergingDetailDao.getByMergingId(userMergingId);
    }

    public MergeAction queryMergeAction(String userMergingId) {
        UserMerging mergingLog = queryMergeOverview(userMergingId);
        List<UserMergingDetail> details = queryMergeDetail(userMergingId);
        return new MergeAction()
                .setUserMergingId(mergingLog.getUserMergingId())
                .setSourceIdentificationId(mergingLog.getSourceIdentificationId())
                .setTargetIdentificationId(mergingLog.getTargetIdentificationId())
                .setStatus(mergingLog.getStatus())
                .setActionList(details.stream().map(action -> new PartitionAction()
                        .setUserMergingId(action.getUserMergingId())
                        .setUserMergingDetailId(action.getUserMergingDetailId())
                        .setDatasourceId(action.getDatasourceId())
                        .setStatus(action.getStatus())
                        .setUserMergingTimestamp(action.getUserMergingTimestamp())
                ).collect(Collectors.toList()));

    }

}
